﻿#pragma once

#include "platforms.h"
#ifdef _ANIP_WINDOWS

// enable visual styles
#pragma comment(linker, \
	"\"/manifestdependency:type='win32' " \
	"name='Microsoft.Windows.Common-Controls' " \
	"version='6.0.0.0' processorArchitecture='*' " \
	"publicKeyToken='6595b64144ccf1df' language='*'\"")
#endif

#include <string>
#include <cassert>
#include <exception>
#include <stdexcept>
#include <functional>
#include <iostream>
#include <vector>
#include "flags/flags.hpp"

namespace anip
{
	constexpr unsigned int version = 10;

	// cf. https://www.glfw.org/faq.html#41---how-do-i-create-an-opengl-30-context
	constexpr int GLVersionMajor = 4;
	constexpr int GLVersionMinor = 1;

	// not sure if it is good, but we do use them very frequently
	typedef std::string string;
	typedef std::wstring wstring;

	class GLObject
	{
	public:
		virtual unsigned int id() const = 0;
		virtual void finalize() = 0;

		bool operator <(const GLObject& a) const
		{
			return id() < a.id();
		}
	};

	enum class MouseButton
	{
		None = -1,
		Left = 0, // GLFW_MOUSE_BUTTON_LEFT
		Right = 1, // GLFW_MOUSE_BUTTON_RIGHT
		Middle = 2, // GLFW_MOUSE_BUTTON_MIDDLE
	};

	enum class ButtonAction
	{
		None = -1,
		Up = 0, // GLFW_RELEASE
		Down = 1, // GLFW_PRESS
		Repeat = 2, //GLFW_REPEAT
	};

	enum class Key
	{
		None = 0,
		Space = 32,
		Apostrophe = 39,  /* ' */
		Comma = 44,  /* , */
		Minus = 45,  /* - */
		Period = 46,  /* . */
		Slash = 47,  /* / */
		N0 = 48,
		N1 = 49,
		N2 = 50,
		N3 = 51,
		N4 = 52,
		N5 = 53,
		N6 = 54,
		N7 = 55,
		N8 = 56,
		N9 = 57,
		Semicolon = 59,  /* , */
		Equal = 61,  /* = */
		A = 65,
		B = 66,
		C = 67,
		D = 68,
		E = 69,
		F = 70,
		G = 71,
		H = 72,
		I = 73,
		J = 74,
		K = 75,
		L = 76,
		M = 77,
		N = 78,
		O = 79,
		P = 80,
		Q = 81,
		R = 82,
		S = 83,
		T = 84,
		U = 85,
		V = 86,
		W = 87,
		X = 88,
		Y = 89,
		Z = 90,
		LeftBracket = 91,  /* [ */
		Backslash = 92,  /* \ */
		RightBracket = 93,  /* ] */
		GraveAccent = 96,  /* ` */
		World1 = 161, /* Non-Us #1 */
		World2 = 162, /* Non-Us #2 */

		/* Function Keys */
		Escape = 256,
		Enter = 257,
		Tab = 258,
		Backspace = 259,
		Insert = 260,
		Delete = 261,
		Right = 262,
		Left = 263,
		Down = 264,
		Up = 265,
		Pageup = 266,
		Pagedown = 267,
		Home = 268,
		End = 269,
		Capslock = 280,
		Scrolllock = 281,
		Numlock = 282,
		Printscreen = 283,
		Pause = 284,
		F1 = 290,
		F2 = 291,
		F3 = 292,
		F4 = 293,
		F5 = 294,
		F6 = 295,
		F7 = 296,
		F8 = 297,
		F9 = 298,
		F10 = 299,
		F11 = 300,
		F12 = 301,
		F13 = 302,
		F14 = 303,
		F15 = 304,
		F16 = 305,
		F17 = 306,
		F18 = 307,
		F19 = 308,
		F20 = 309,
		F21 = 310,
		F22 = 311,
		F23 = 312,
		F24 = 313,
		F25 = 314,
		Kp0 = 320,
		Kp1 = 321,
		Kp2 = 322,
		Kp3 = 323,
		Kp4 = 324,
		Kp5 = 325,
		Kp6 = 326,
		Kp7 = 327,
		Kp8 = 328,
		Kp9 = 329,
		KpDecimal = 330,
		KpDivide = 331,
		KpMultiply = 332,
		KpSubtract = 333,
		KpAdd = 334,
		KpEnter = 335,
		KpEqual = 336,
		LeftShift = 340,
		LeftControl = 341,
		LeftAlt = 342,
		LeftSuper = 343,
		RightShift = 344,
		RightControl = 345,
		RightAlt = 346,
		RightSuper = 347,
		Menu = 348
	};

	enum class InitStatus
	{
		Uninitialized,
		Initialized,
		Finalized
	};

	/// <summary>
	/// Returns the time since the application started, in seconds.
	/// </summary>
	double time();

	void _error_callback(int error, const char* description);

	void init();

	void finalize();

	InitStatus initStatus();
	
	namespace fs
	{
		bool fileExists(const string& name);
		
		string filename(const string& path, bool ext = true);
		string extension(const string& path);
		string removeExtension(const string& path);
		
		wstring path2ws(const string& path);
		string ws2path(const wstring& path);
	}

	/// <summary>
	/// Converts a UTF-8 string into a wide string, which can be used throughout
	/// anip. Includes a kind of byte-order check (or really a workaround?) that 
	/// seems to be working.
	/// </summary>
	wstring utf8s2ws(const string& str);

	/// <summary>
	/// Converts a wide string into a UTF-8 string.
	/// </summary>
	string ws2utf8s(const wstring& str);

	std::vector<wstring> split(const wstring& str, const wstring& delim);
	std::vector<string> split(const string& str, const string& delim);

	wstring toString(int x);
	wstring toString(float x);
	wstring toString(double x);

	bool parseInt(wstring s, int& out, int radix = 10);
	bool parseFloat(wstring s, float& out);
	bool parseDouble(wstring s, double& out);

	string readFile(const string& path);
	wstring readUTF8File(const string& path);

	template<typename T>
	void binaryWrite(const T& val, std::ostream& o)
	{
		static_assert(std::is_scalar_v<T>, "T must be a scalar type");
		o.write((char*)&val, sizeof(T));
	}

	template<typename T>
	void binaryRead(T& val, std::istream& i)
	{
		static_assert(std::is_scalar_v<T>, "T must be a scalar type");
		i.read((char*)&val, sizeof(T));
	}

	template<typename T>
	T binaryRead(std::istream& i)
	{
		T val;
		binaryRead(val, i);
		return val;
	}

	template<>
	void binaryWrite(const string& val, std::ostream& o);

	template<>
	void binaryWrite(const wstring& val, std::ostream& o);

	template<>
	void binaryRead(string& val, std::istream& i);

	template<>
	void binaryRead(wstring& val, std::istream& i);

	void debug(const char* format, ...);

	void debug(const wchar_t* format, ...);

	/// <summary>
	/// Use as the type of a dummy parameter for SFINAE templates to indicate
	/// lower overload priority; e.g. fun(T a, _Specialization) will have
	/// higher priority over fun(U a, _Fallback) in case both substitutions
	/// are sucessful. Often, these are used in a helper function (i.e. funImpl)
	/// and a forwarding function is used as the public interface.
	/// </summary>
	struct _Fallback {};

	/// <summary>
	/// Use as the type of a dummy parameter for SFINAE templates to indicate
	/// higher overload priority; e.g. fun(T a, _Specialization) will have
	/// higher priority over fun(U a, _Fallback) in case both substitutions
	/// are sucessful. Often, these are used in a helper function (i.e. funImpl)
	/// and a forwarding function is used as the public interface.
	/// </summary>
	struct _Specialization : _Fallback {};
	
#ifdef USE_ERROR_CHECK

	void _errorCheck(const char* file, const char* line);

#define _STR(x) #x
#define __STR(x) _STR(x)
#define ERROR_CHECKED ;anip::_errorCheck(__FILE__, __STR(__LINE__))

#else

#define ERROR_CHECKED

#endif

}

// Macros

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-value"

#define ANIP_FAIL(msg) (assert(false), throw (msg))

#pragma clang diagnostic pop

// Interestingly, this also works when `fun` that doesn't return a value
// since you can return `void()` from a void function.
#define ANIP_forwardTo(fun) ([&](auto... x) { return fun(x...); })
#define ANIP_forwardSkipping1(fun) ([&](auto, auto... x) { return fun(x...); })

#include "amath.h"
#include "color/colors.h"